﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Health;
using SDS.SPHealthAnalyzerRules.Delegation.Helpers;

namespace SDS.SPHealthAnalyzerRules.Delegation.Rules
{
    /// <summary>
    /// Validates that SPN's exist for webapps with Kerb authentication, for every combination of:
    /// - hostname/FQDN/AAM
    /// - port (if not 80/443)
    /// - service account name
    /// </summary>
    public class WebAppAppPools_Using_DomainAccount : SPHealthAnalysisRule
    {
        private Guid CorrelationToken = Guid.NewGuid();

        [System.Diagnostics.DebuggerDisplay("{WebAppName}/{Zones} using {ServiceAccount}")]
        private struct TestParameter
        {
            public string WebAppName;
            public string Zones;
            public string ServiceAccount;
        }
        readonly private List<TestParameter> Errors = new List<TestParameter>();
        //readonly public List<TestParameter> CheckLogs = new List<TestParameter>();

        public override SPHealthCategory Category { get { return SPHealthCategory.Configuration; } }
        public override SPHealthCheckErrorLevel ErrorLevel { get { return SPHealthCheckErrorLevel.Error; } }

        public override SPHealthAnalysisRuleAutomaticExecutionParameters AutomaticExecutionParameters
        {
            get
            {
                return new SPHealthAnalysisRuleAutomaticExecutionParameters()
                {
                    Schedule = SPHealthCheckSchedule.Daily,
                    Scope = SPHealthCheckScope.Any,
                    ServiceType = typeof(SPWebService),
                    RepairAutomatically = false
                };
            }
        }

        public override SPHealthCheckStatus Check()
        {
            // return variable, defualt value
            SPHealthCheckStatus outval = SPHealthCheckStatus.Failed;

            // log
            LoggingService.Write(LoggingService.Category_WebApp, TraceSeverity.Verbose, "Executing Health Check", CorrelationToken);

            // running as the system account
            Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(delegate { doCheck(); });

            // set return value
            outval = Errors.Count == 0
                 ? SPHealthCheckStatus.Passed
                 : SPHealthCheckStatus.Failed;

            // log
            LoggingService.Write(
                LoggingService.Category_WebApp,
                TraceSeverity.Verbose,
                String.Format("Health Check completed with outcome {0}", outval.ToString()),
                CorrelationToken);

            return outval;
        }

        public override string Summary { get { return "DELEGATION: Kerberos enabled Web Applications are hosted with local accounts"; } }
        public override string Explanation
        {
            get {
                var x = Errors.Select(e => String.Format(@"WebApp [{0}] using account [{1}] with Kerberos zone(s) [{2}]",
                                                          e.WebAppName,
                                                          e.ServiceAccount,
                                                          e.Zones))
                              .ToArray();
                return String.Format(
                    @"{0}Kerberos authentication requires an SPN, which cannot use local accounts." +
                    @"{0}" +
                    @"{0}The following Web Applications are configured for Kerberos Authentication, but are NOT using a domain account." +
                    @"{0}{1}",
                    
                    Environment.NewLine,
                    String.Join(Environment.NewLine, x)
                    ).Trim();
            }
        }
        public override string Remedy
        {
            get { return String.Format("Either disable Kerberos authentication, or configure the web application to use a domain account for its application pool.");  }
        }



        private void doCheck()
        {
            try
            {

                // obtain the local SPServer object
                // no need to query every server, since the job will execute on every server running SPWebService (as per execution parameters)
                var Server = Microsoft.SharePoint.Administration.SPServer.Local;

                // looking for webapps using kerberos, where app pool service account starts with server name (local account)
                // log each instance to the error list
                Server.WebApplications()
                        .UsingKerberos()
                        .Where(WebApp => WebApp.ApplicationPool.Username.ToLower().StartsWith(Server.Name.ToLower()))
                        .ToList()
                        .ForEach(wa =>
                        {
                            //if (wa.ApplicationPool.Username.ToLower().StartsWith(Server.Name.ToLower()))
                                Errors.Add(new TestParameter()
                                    {
                                        WebAppName = wa.DisplayName,
                                        ServiceAccount = wa.ApplicationPool.Username,
                                        Zones = String.Join(", ", wa.IisSettings.Where(i => i.Value.AuthenticationMode == System.Web.Configuration.AuthenticationMode.Windows &&
                                                                                            !i.Value.DisableKerberos)
                                                                                .Select(i => i.Key.ToString())
                                                                                .ToArray())
                                    });
                            //else
                            //    CheckLogs.Add(new TestParameter()
                            //        {
                            //            WebAppName = wa.DisplayName,
                            //            ServiceAccount = wa.ApplicationPool.Username,
                            //            Zones = String.Join(", ", wa.IisSettings.Where(i => i.Value.AuthenticationMode == System.Web.Configuration.AuthenticationMode.Windows &&
                            //                                                                !i.Value.DisableKerberos)
                            //                                                    .Select(i => i.Key.ToString())
                            //                                                    .ToArray())
                            //        });
                        });

            } // try
            catch (Exception ex)
            {
                LoggingService.Write(
                    LoggingService.Category_WebApp,
                    TraceSeverity.High,
                    String.Format("Health Check encountered the following exception: {0}", ex.Message),
                    CorrelationToken);
            }

        } // doCheck()

    } // class
} // namespace
